/** <a href="http://www.cpupk.com/decompiler">Eclipse Class Decompiler</a> plugin, Copyright (c) 2017 Chen Chao. */
/*
 * Copyright 2014-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.springframework.session.web.http;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import com.springframework.session.Session;
import com.springframework.session.SessionRepository;
import com.springframework.session.web.OncePerRequestFilter;

/**
 * Switches the {@link javax.servlet.http.HttpSession} implementation to be
 * backed by a {@link com.springframework.session.Session}.
 *
 * The {@link SessionRepositoryFilter} wraps the
 * {@link javax.servlet.http.HttpServletRequest} and overrides the methods to
 * get an {@link javax.servlet.http.HttpSession} to be backed by a
 * {@link com.springframework.session.Session} returned by the
 * {@link com.springframework.session.SessionRepository}.
 *
 * The {@link SessionRepositoryFilter} uses a {@link HttpSessionStrategy}
 * (default {@link CookieHttpSessionStrategy} to bridge logic between an
 * {@link javax.servlet.http.HttpSession} and the
 * {@link com.springframework.session.Session} abstraction. Specifically:
 *
 * <ul>
 * <li>The session id is looked up using
 * {@link HttpSessionStrategy#getRequestedSessionId(javax.servlet.http.HttpServletRequest)}
 * . The default is to look in a cookie named SESSION.</li>
 * <li>The session id of newly created
 * {@link org.springframework.session.ExpiringSession} is sent to the client
 * using
 * <li>The client is notified that the session id is no longer valid with
 * {@link HttpSessionStrategy#onInvalidateSession(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
 * </li>
 * </ul>
 *
 * <p>
 * The SessionRepositoryFilter must be placed before any Filter that access the
 * HttpSession or that might commit the response to ensure the session is
 * overridden and persisted properly.
 * </p>
 *
 * @param <S>
 *            the {@link ExpiringSession} type.
 * @since 1.0
 * @author Rob Winch
 */
public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {

	/**
	 * The default filter order.
	 */
	public static final int DEFAULT_ORDER = Integer.MIN_VALUE + 50;

	private final SessionRepository<S> sessionRepository;

	private ServletContext servletContext;

	private HttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy();

	/**
	 * Creates a new instance.
	 *
	 * @param sessionRepository
	 *            the <code>SessionRepository</code> to use. Cannot be null.
	 */
	public SessionRepositoryFilter(SessionRepository<S> sessionRepository) {
		if (sessionRepository == null) {
			throw new IllegalArgumentException("sessionRepository cannot be null");
		}
		this.sessionRepository = sessionRepository;
	}

	/**
	 * Sets the {@link HttpSessionStrategy} to be used. The default is a
	 * {@link CookieHttpSessionStrategy}.
	 *
	 * @param httpSessionStrategy
	 *            the {@link HttpSessionStrategy} to use. Cannot be null.
	 */
	public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) {
		if (httpSessionStrategy == null) {
			throw new IllegalArgumentException("httpSessionStrategy cannot be null");
		}
		this.httpSessionStrategy = new MultiHttpSessionStrategyAdapter(httpSessionStrategy);
	}

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		SessionRepositoryRequestWrapper<S> wrappedRequest = new SessionRepositoryRequestWrapper<S>(request, response,
				this.servletContext, sessionRepository, httpSessionStrategy);
		SessionRepositoryResponseWrapper<S> wrappedResponse = new SessionRepositoryResponseWrapper<S>(wrappedRequest,
				response);

		HttpServletRequest strategyRequest = this.httpSessionStrategy.wrapRequest(wrappedRequest, wrappedResponse);
		HttpServletResponse strategyResponse = this.httpSessionStrategy.wrapResponse(wrappedRequest, wrappedResponse);

		try {
			filterChain.doFilter(strategyRequest, strategyResponse);
		} finally {
			wrappedRequest.commitSession();
		}
	}

	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}

	/**
	 * A delegating implementation of {@link MultiHttpSessionStrategy}.
	 */
	static class MultiHttpSessionStrategyAdapter implements HttpSessionStrategy {
		private HttpSessionStrategy delegate;

		/**
		 * Create a new {@link MultiHttpSessionStrategyAdapter} instance.
		 * 
		 * @param delegate
		 *            the delegate HTTP session strategy
		 */
		MultiHttpSessionStrategyAdapter(HttpSessionStrategy delegate) {
			this.delegate = delegate;
		}

		public String getRequestedSessionId(HttpServletRequest request) {
			return this.delegate.getRequestedSessionId(request);
		}

		public void onNewSession(Session session, HttpServletRequest request, HttpServletResponse response) {
			this.delegate.onNewSession(session, request, response);
		}

		public void onInvalidateSession(HttpServletRequest request, HttpServletResponse response) {
			this.delegate.onInvalidateSession(request, response);
		}

		public HttpServletRequest wrapRequest(HttpServletRequest request, HttpServletResponse response) {
			return request;
		}

		public HttpServletResponse wrapResponse(HttpServletRequest request, HttpServletResponse response) {
			return response;
		}
	}
}
